package com.gzc.just.play.last.war.sceneserver.gameobject.unit;

import com.gzc.just.play.last.war.sceneserver.gameobject.base.GameObjectCamp;
import com.gzc.just.play.last.war.sceneserver.gameobject.base.GameObjectType;
import com.gzc.just.play.last.war.sceneserver.gameobject.unit.GameUnitListener;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

/**
 * Hero类的单元测试
 * 
 * @author AI Assistant
 * @version 1.0.0
 * @since 2025-11-25
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("英雄类测试")
class HeroTest {

    private Hero hero;
    
    @Mock
    private GameUnitListener mockListener;
    
    @BeforeEach
    void setUp() {
        hero = new Hero(
            12345L,              // objectId
            100001L,             // userId
            1,                   // heroTypeId
            "Test Hero",         // heroName
            "TestPlayer",        // nickname
            GameObjectCamp.TEAM_1, // camp
            1000,                // maxHealth
            500                  // maxMana
        );
    }
    
    @Test
    @DisplayName("测试英雄创建和基本属性")
    void testHeroCreation() {
        // 验证基本属性
        assertEquals(12345L, hero.getObjectId());
        assertEquals(100001L, hero.getUserId());
        assertEquals(1, hero.getHeroTypeId());
        assertEquals("Test Hero", hero.getHeroName());
        assertEquals("TestPlayer", hero.getNickname());
        assertEquals(GameObjectType.HERO, hero.getObjectType());
        assertEquals(GameObjectCamp.TEAM_1, hero.getCamp());
        
        // 验证初始等级和经验
        assertEquals(1, hero.getLevel());
        assertEquals(0L, hero.getExperience());
        assertEquals(1000L, hero.getNextLevelExp());
        
        // 验证初始资源
        assertEquals(0, hero.getGold());
        assertEquals(0, hero.getGems());
        assertEquals(0, hero.getGoldSpent());
        
        // 验证战斗统计
        assertEquals(0, hero.getKillStreak());
        assertEquals(0, hero.getMaxKillStreak());
        assertEquals(0, hero.getAssistCount());
        
        // 验证选择状态
        assertFalse(hero.isHeroChoosed());
        assertFalse(hero.isLoadComplete());
        assertFalse(hero.isOffline());
        
        // 验证队伍信息
        assertEquals(0, hero.getTeamId());
        assertEquals(0, hero.getBattlePosition());
        
        // 验证基础属性
        assertEquals(1000, hero.getMaxHealth());
        assertEquals(1000, hero.getCurrentHealth());
        assertEquals(500, hero.getMaxMana());
        assertEquals(500, hero.getCurrentMana());
    }
    
    @Test
    @DisplayName("测试英雄初始化")
    void testHeroInitialization() {
        hero.initialize();
        assertTrue(hero.isActive());
        assertTrue(hero.isAlive());
        assertFalse(hero.isDead());
    }
    
    @Test
    @DisplayName("测试英雄销毁")
    void testHeroDestroy() {
        hero.initialize();
        hero.destroy();
        
        assertFalse(hero.isActive());
        assertFalse(hero.isAlive());
        assertTrue(hero.isDead());
    }
    
    @Test
    @DisplayName("测试经验获得和升级")
    void testExperienceAndLevelUp() {
        hero.initialize();
        
        // 获得少量经验，不足以升级
        hero.gainExperience(500);
        assertEquals(500L, hero.getExperience());
        assertEquals(1, hero.getLevel());
        
        // 获得足够经验升级
        hero.gainExperience(600); // 总共1100，超过需要的1000
        assertEquals(2, hero.getLevel());
        assertEquals(100L, hero.getExperience()); // 1100 - 1000
        assertEquals(1200L, hero.getNextLevelExp()); // 1000 * 1.2
        
        // 验证升级带来的属性提升
        assertTrue(hero.getMaxHealth() > 1000); // 基础血量提升
        assertTrue(hero.getMaxMana() > 500); // 基础魔力提升
    }
    
    @Test
    @DisplayName("测试金币管理")
    void testGoldManagement() {
        hero.initialize();
        
        // 获得金币
        hero.gainGold(100);
        assertEquals(100, hero.getGold());
        
        // 消费金币
        assertTrue(hero.spendGold(50));
        assertEquals(50, hero.getGold());
        assertEquals(50, hero.getGoldSpent());
        
        // 尝试消费超出余额的金币
        assertFalse(hero.spendGold(100));
        assertEquals(50, hero.getGold());
        assertEquals(50, hero.getGoldSpent());
        
        // 测试负值消费
        assertFalse(hero.spendGold(-10));
        assertEquals(50, hero.getGold());
    }
    
    @Test
    @DisplayName("测试连杀和助攻")
    void testKillStreakAndAssist() {
        hero.initialize();
        
        // 增加连杀
        hero.addKillStreak();
        assertEquals(1, hero.getKillStreak());
        assertEquals(1, hero.getMaxKillStreak());
        
        hero.addKillStreak();
        assertEquals(2, hero.getKillStreak());
        assertEquals(2, hero.getMaxKillStreak());
        
        // 重置连杀
        hero.resetKillStreak();
        assertEquals(0, hero.getKillStreak());
        assertEquals(2, hero.getMaxKillStreak()); // 最高记录保持不变
        
        // 添加助攻
        hero.addAssist();
        assertEquals(1, hero.getAssistCount());
        
        hero.addAssist();
        assertEquals(2, hero.getAssistCount());
    }
    
    @Test
    @DisplayName("测试选择状态")
    void testSelectionState() {
        // 测试英雄选择
        assertFalse(hero.isHeroChoosed());
        hero.selectHero();
        assertTrue(hero.isHeroChoosed());
        
        // 测试加载完成
        assertFalse(hero.isLoadComplete());
        hero.completeLoading();
        assertTrue(hero.isLoadComplete());
        
        // 测试离线状态
        assertFalse(hero.isOffline());
        hero.setOffline(true);
        assertTrue(hero.isOffline());
        
        hero.setOffline(false);
        assertFalse(hero.isOffline());
    }
    
    @Test
    @DisplayName("测试队伍信息设置")
    void testTeamInfo() {
        // 设置队伍ID
        hero.setTeamId(1);
        assertEquals(1, hero.getTeamId());
        
        // 设置战斗位置
        hero.setBattlePosition(3);
        assertEquals(3, hero.getBattlePosition());
    }
    
    @Test
    @DisplayName("测试战斗力计算")
    void testBattlePowerCalculation() {
        hero.initialize();
        
        // 初始战斗力
        double initialPower = hero.calculateBattlePower();
        assertTrue(initialPower > 0);
        assertEquals(initialPower, hero.getBattlePower());
        
        // 消费金币（代表装备提升）后战斗力应该增加
        hero.gainGold(1000);
        hero.spendGold(500);
        
        double newPower = hero.calculateBattlePower();
        assertTrue(newPower > initialPower);
        
        // 升级后战斗力应该增加
        hero.gainExperience(2000); // 升2级
        double levelUpPower = hero.calculateBattlePower();
        assertTrue(levelUpPower > newPower);
    }
    
    @Test
    @DisplayName("测试英雄死亡和复活")
    void testHeroDeathAndRevival() {
        hero.initialize();
        
        // 设置一个连杀数
        hero.addKillStreak();
        hero.addKillStreak();
        assertEquals(2, hero.getKillStreak());
        
        // 死亡
        hero.takeDamage(2000); // 超过最大生命值
        assertTrue(hero.isDead());
        assertEquals(0, hero.getCurrentHealth());
        
        // 连杀应该被重置
        assertEquals(0, hero.getKillStreak());
        
        // 复活
        hero.reviveHero(50); // 复活时恢复50%生命值
        assertFalse(hero.isDead());
        assertEquals(500, hero.getCurrentHealth()); // 1000的50%
        
        // 连杀应该仍然为0（复活时重置）
        assertEquals(0, hero.getKillStreak());
    }
    
    @Test
    @DisplayName("测试英雄特有方法")
    void testHeroSpecificMethods() {
        hero.initialize();
        
        // isHero应该返回true
        assertTrue(hero.isHero());
        
        // isNPC应该返回false
        assertFalse(hero.isNPC());
    }
    
    @Test
    @DisplayName("测试英雄心跳处理")
    void testHeroHeartbeat() {
        hero.initialize();
        
        long currentTime = System.currentTimeMillis();
        long deltaTime = 100;
        
        // 正常情况下的心跳处理
        hero.processHeartbeat(currentTime, deltaTime);
        assertTrue(hero.isActive());
        
        // 设置为离线状态后再处理心跳
        hero.setOffline(true);
        hero.processHeartbeat(currentTime + 100, deltaTime);
        
        // 离线状态下应该正常处理
        assertTrue(hero.isOffline());
    }
    
    @Test
    @DisplayName("测试英雄toString方法")
    void testHeroToString() {
        String heroString = hero.toString();
        
        // 验证字符串包含关键信息
        assertTrue(heroString.contains("id=12345"));
        assertTrue(heroString.contains("userId=100001"));
        assertTrue(heroString.contains("heroId=1"));
        assertTrue(heroString.contains("heroName=Test Hero"));
        assertTrue(heroString.contains("nickname=TestPlayer"));
        assertTrue(heroString.contains("level=1"));
        assertTrue(heroString.contains("hp=1000/1000"));
        assertTrue(heroString.contains("gold=0"));
        assertTrue(heroString.contains("killStreak=0"));
        assertTrue(heroString.contains("alive=true"));
        assertTrue(heroString.contains("dead=false"));
    }
    
    @Test
    @DisplayName("测试经验系统细节")
    void testExperienceSystemDetails() {
        hero.initialize();
        
        // 死亡状态下不能获得经验
        hero.die();
        assertTrue(hero.isDead());
        long oldExp = hero.getExperience();
        hero.gainExperience(100);
        assertEquals(oldExp, hero.getExperience()); // 经验没增加
        
        // 离线状态下不能获得经验
        hero.reviveHero(100); // 复活
        hero.setOffline(true);
        oldExp = hero.getExperience();
        hero.gainExperience(100);
        assertEquals(oldExp, hero.getExperience()); // 经验没增加
        
        // 恢复正常状态后可以获得经验
        hero.setOffline(false);
        hero.gainExperience(100);
        assertEquals(100L, hero.getExperience());
    }
    
    @Test
    @DisplayName("测试金币系统细节")
    void testGoldSystemDetails() {
        hero.initialize();
        
        // 死亡状态下不能获得金币
        hero.die();
        assertTrue(hero.isDead());
        int oldGold = hero.getGold();
        hero.gainGold(100);
        assertEquals(oldGold, hero.getGold()); // 金币没增加
        
        // 复活后可以正常获得金币
        hero.reviveHero(100);
        hero.gainGold(100);
        assertEquals(100, hero.getGold());
    }
    
    @Test
    @DisplayName("测试多级升级")
    void testMultipleLevelUps() {
        hero.initialize();
        
        // 一次获得大量经验，连续升级多次
        hero.gainExperience(5000); // 足够升多级
        
        // 验证等级和经验计算正确
        assertTrue(hero.getLevel() > 1);
        
        // 验证属性提升
        assertTrue(hero.getMaxHealth() > 1000);
        assertTrue(hero.getMaxMana() > 500);
        
        // 验证经验计算：通过模拟升级过程计算预期值
        // 模拟升级流程以计算准确的经验值
        long usedExp = 0;
        long simNextLevelExp = 1000;
        for (int level = 1; level < hero.getLevel(); level++) {
            usedExp += simNextLevelExp;
            simNextLevelExp = (long)(simNextLevelExp * 1.2);
        }
        
        long expectedRemainingExp = 5000 - usedExp;
        long expectedNextLevelExp = simNextLevelExp;
        
        // 由于浮点数运算可能存在精度问题，使用容差比较
        assertEquals(expectedRemainingExp, hero.getExperience(), 1);
        assertEquals(expectedNextLevelExp, hero.getNextLevelExp(), 1);
    }
}